#include <os/timer.h>
#include <os/clock.h>
#include <os/safety.h>
#include <os/sleep.h>
#include <os/task.h>
#include <sys/time.h>
#include <arch/interrupt.h>
#include <arch/x86.h>
#include <arch/cpu.h>
#include <lib/type.h>
#include <lib/list.h>
#include <lib/assert.h>
#include <lib/errno.h>

DEFINE_SPIN_LOCK(timer_lock);

timer_group_t timer_group[CPU_NUM_MAX];
uint32_t timer_id[CPU_NUM_MAX];

// idle timer
static void TimerIdleHandler()
{
    clock_t time;
    timer_t *temp;
    cpuid_t cpu = CpuGetMyId();

    time = timer_group[cpu].timer_idle.timeout;
    // reset all timer
    timer_ticks -= time;
    list_traversal_all_owner_to_next(temp, &timer_group[cpu].timer_list_head, list)
    {
        if (temp != &timer_group[cpu].timer_idle)
        {
            temp->timeout -= time;
        }
    }
    // add timer idle to timer list again
    TimerAdd(&timer_group[cpu].timer_idle);
}

void TimersInit()
{
    timer_group_t *group;
#ifdef ENABLE_SMP
    uint32_t num = cpunum;
#else
    uint32_t num = 1;
#endif

    SpinLockInit(&timer_lock);

    for (int i = 0; i < num; i++)
    {
        group = &timer_group[i];
        group->mini_timeout = 0;
        group->timer_num = 0;
        list_init(&group->timer_list_head);
        TimerInit(&group->timer_idle, TIMER_IDLE_TIMEOUT, NULL, TimerIdleHandler);
        TimerAdd(&group->timer_idle);
    }
    KPrint("[timer] tiemr init ok!\n");
}

void TimerInit(timer_t *timer, uint64_t timeout, void *arg, timer_callback_t callback)
{
    timer->arg = arg;
    timer->callback = callback;
#ifdef ENABLE_SMP
    timer->id = CpuGetMyId();
#else
    timer->id = 0;
#endif
    timer->timeout = timeout + timer_ticks;
    list_init(&timer->list);
}

void TimerAdd(timer_t *timer)
{
    timer_t *temp_timer;
#ifdef ENABLE_SMP
    cpuid_t cpu = CpuGetMyId();
#else
    cpuid_t cpu = 0; // first timer is used to as single timer
#endif

    uint32_t mini_timeout = timer_group[cpu].mini_timeout;
    uint32_t eflags=InterruptDisableStore();
    if (!list_find(&timer->list, &timer_group[cpu].timer_list_head))
    {
        // first timer,mini_timeout is timer timeout
        if (list_empty(&timer_group[cpu].timer_list_head))
        {
            // KPrint("[timer] add timer to first!\n");
            list_add_head(&timer->list, &timer_group[cpu].timer_list_head);
            timer_group[cpu].mini_timeout = timer->timeout;
        }
        else
        {
            // if timer timeout no is last small
            if (timer->timeout > timer_group[cpu].mini_timeout)
            {
                // KPrint("[timer] add timer to fit pos!\n");

                if (list_empty(&timer_group[cpu].timer_list_head))
                {
                    list_add_after(&timer->list, &timer_group[cpu].timer_list_head);
                    timer_group[cpu].mini_timeout = timer->timeout;
                }

                //  add to fit position
                list_traversal_all_owner_to_next(temp_timer, &timer_group[cpu].timer_list_head, list)
                {
                    if (timer->timeout <= temp_timer->timeout) // from previous timer start timer
                    {
                        list_add_before(&timer->list, &temp_timer->list);
                        break;
                    }
                }
            }
            else
            {
                // KPrint("[timer] add timer to first\n");
                //   if timer timeout is last small
                //   add to after first timer
                list_add_after(&timer->list, &timer_group[cpu].timer_list_head);
                timer_group[cpu].mini_timeout = timer->timeout;
            }
        }
        timer_group[cpu].timer_num++;
    }
    InterruptEnableRestore(eflags);
    KPrint("[timer] timer add ok! arg %x timeout %d timer ticks %d minitimeout %d\n",(uint32_t)timer->arg, (uint32_t)timer->timeout,(uint32_t)timer_ticks,timer_group[cpu].mini_timeout);
}

void TimerDump()
{
    timer_t *timer, *next;
    #ifdef ENABLE_SMP
    cpuid_t cpu = CpuGetMyId();
#else
    cpuid_t cpu = 0; // first timer is used to as single timer
#endif
    uint32_t eflags=InterruptDisableStore();
    list_traversal_all_owner_to_next_safe(timer, next, &timer_group[cpu].timer_list_head, list)
    {
        KPrint("timer: timeout %d\n", timer->timeout);
    }
    InterruptEnableRestore(eflags);
}

void TimerDel(timer_t *timer)
{
    cpuid_t cpu = CpuGetMyId();
    uint32_t eflags=InterruptDisableStore();
    if (list_find(&timer->list, &timer_group[cpu].timer_list_head))
    {
        list_del(&timer->list);
    }
    InterruptEnableRestore(eflags);
}

int TimerAlive(timer_t *timer)
{
    int alive = 0;
    cpuid_t cpu = CpuGetMyId();

    uint32_t eflags=InterruptDisableStore();
    if (list_find(&timer->list, &timer_group[cpu].timer_list_head))
    {
        alive = 1;
    }
    InterruptEnableRestore(eflags);
    return alive;
}

// modify timer timeout
void TimerModify(timer_t *timer, uint64_t timeout)
{
    timer->timeout = timer_ticks + timeout;
}

timer_t *TimerFind(uint64_t id)
{
    timer_t *timer;
    cpuid_t cpu = CpuGetMyId();
    uint32_t eflags=InterruptDisableStore();
    list_traversal_all_owner_to_next(timer, &timer_group[cpu].timer_list_head, list)
    {
        if (timer->id == id)
        {
            InterruptEnableRestore(eflags);
            return timer;
        }
    }
    InterruptEnableRestore(eflags);
    return NULL;
}

int TimerCancel(timer_t *timer)
{
    if (timer)
    {
        if (TimerAlive(timer))
        {
            TimerDel(timer);
        }
    }
}

static void TimerDoAction(timer_t *timer)
{
    list_del(&timer->list);

    if (!timer->callback)
        return;

    timer->callback(timer, timer->arg);

}

void TimerUpdateTicks()
{
    timer_t *timer, *next;
    cpuid_t cpu = CpuGetMyId();

    if (timer_ticks <= timer_group[cpu].mini_timeout)
        return; // no timeout

    uint32_t eflags=InterruptDisableStore(eflags);
    list_traversal_all_owner_to_next_safe(timer, next, &timer_group[cpu].timer_list_head, list)
    {
        // timeout
        if (timer->timeout >= timer_ticks)
            break;
        TimerDoAction(timer);
    }

    list_traversal_all_owner_to_next(timer, &timer_group[cpu].timer_list_head, list)
    {
        if (timer->timeout >= timer_ticks)
            break;
    }

    // update mini_timeout to current timer timeout
    // current timer available
    timer_group[cpu].mini_timeout = timer->timeout;

    InterruptEnableRestore(eflags);
}

int SysUSleep(timeval_t *inv, timeval_t *outv)
{
    timeval_t tv;
    uint64_t ticks;

    if (!inv)
        return -EINVAL;
    if (MemCopyToUser(&tv, inv, sizeof(timeval_t)))
        return -EFAULT;
    if (tv.tv_usec >= 1000000 || tv.tv_sec < 0 || tv.tv_usec < 0)
        return -EINVAL;
    // if <2ms just use delay
    if (tv.tv_usec < 2000L && (!tv.tv_sec))
    {
        UDelay(tv.tv_usec);
        return 0;
    }
    // task sleep by figure ticks result
    ticks = TimevalToSysticks(&tv);
    ticks = TaskSleepByTicks(ticks);
    // return val>0
    if (ticks > 0)
    {
        if (outv)
        {
            SysticksToTimeval(ticks, &tv);
            if (MemCopyToUser(outv, &tv, sizeof(timeval_t)))
            {
                return -EFAULT;
            }
        }
        return -EINTR;
    }

    return 0;
}
